package org.argus.management.auth.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.argus.common.core.constant.CacheConstants;
import org.argus.common.core.constant.TokenConstants;
import org.argus.common.core.exception.ServiceException;
import org.argus.common.core.text.Convert;
import org.argus.common.core.utils.StringUtils;
import org.argus.common.redis.service.RedisService;
import org.argus.common.core.enums.StatusEnum;
import org.argus.common.web.core.domain.LoginUser;
import org.argus.common.core.enums.UserType;
import org.argus.common.web.core.security.TokenManager;
import org.argus.common.web.core.utils.ip.IpUtils;
import org.argus.management.auth.AuthConstants;
import org.argus.management.auth.UserConstants;
import org.argus.management.auth.model.RefreshTokenReq;
import org.argus.management.auth.model.RefreshTokenResp;
import org.argus.management.domain.Domain;
import org.argus.management.domain.Role;
import org.argus.management.domain.User;
import org.argus.management.system.mapper.DomainMapper;
import org.argus.management.system.mapper.RoleMapper;
import org.argus.management.system.model.MenuRoute;
import org.argus.management.system.service.DomainService;
import org.argus.management.system.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @Author 刘文/Cliff Liu
 * @Date 2024/4/24
 */
@Service
@Slf4j
public class TokenService {

    @Autowired
    private RedisService redisService;

    @Autowired
    private RecordLogService recordLogService;

    @Autowired
    private UserService userService;

    @Autowired
    private PasswordService passwordService;

    @Autowired
    private TokenManager tokenManager;

    @Autowired
    private DomainMapper domainMapper;

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private DomainService domainService;

    /**
     * 登录
     */
    public LoginUser login(String username, String password)
    {
        // 用户名或密码为空 错误
        if (StringUtils.isAnyBlank(username, password))
        {
            recordLogService.recordLoginInfo(username, TokenConstants.LOGIN_FAIL, "用户/密码必须填写");
            throw new ServiceException("用户/密码必须填写");
        }
        // 密码如果不在指定范围内 错误
        if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
                || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
        {
            recordLogService.recordLoginInfo(username, TokenConstants.LOGIN_FAIL, "用户密码不在指定范围");
            throw new ServiceException("用户密码不在指定范围");
        }
        // 用户名不在指定范围内 错误
        if (username.length() < UserConstants.USERNAME_MIN_LENGTH
                || username.length() > UserConstants.USERNAME_MAX_LENGTH)
        {
            recordLogService.recordLoginInfo(username, TokenConstants.LOGIN_FAIL, "用户名不在指定范围");
            throw new ServiceException("用户名不在指定范围");
        }
        // IP黑名单校验
        String blackStr = Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST));
        if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr()))
        {
            recordLogService.recordLoginInfo(username, TokenConstants.LOGIN_FAIL, "很遗憾，访问IP已被列入系统黑名单");
            throw new ServiceException("很遗憾，访问IP已被列入系统黑名单");
        }
        // 查询用户信息
        User user = userService.getUserInfo(username);

        if (StringUtils.isNull(user) )
        {
            recordLogService.recordLoginInfo(username, TokenConstants.LOGIN_FAIL, "登录用户不存在");
            throw new ServiceException("登录用户：" + username + " 不存在");
        }
        if (StatusEnum.DELETED == user.getStatus())
        {
            recordLogService.recordLoginInfo(username, TokenConstants.LOGIN_FAIL, "对不起，您的账号已被删除");
            throw new ServiceException("对不起，您的账号：" + username + " 已被删除");
        }
        if (StatusEnum.LOCKED == user.getStatus())
        {
            recordLogService.recordLoginInfo(username, TokenConstants.LOGIN_FAIL, "用户已锁定，请联系管理员");
            throw new ServiceException("对不起，您的账号：" + username + " 已锁定");
        }
        passwordService.validate(user, password);
        LoginUser loginUser = createLoginUserInfo(user);
        recordLogService.recordLoginInfo(username, TokenConstants.LOGIN_SUCCESS, "登录成功");
        return loginUser;
    }

    public LoginUser createLoginUserInfo(User user) {
        LoginUser loginUser = new LoginUser();
        loginUser.setUserId(user.getId());
        loginUser.setUsername(user.getUsername());
        loginUser.setUserType(user.getUserType());
        loginUser.setIpaddr(IpUtils.getIpAddr());
        initUserDomainInfo(loginUser);

        List<Role> userRoles = getUserRoles(loginUser);
        Set<Long> roles = userRoles.stream().map(Role::getId).collect(Collectors.toSet());
        roles.add(MenuRoute.USER_MENUS_ROLE_PLACE_HOLDER);
        loginUser.setRoles(roles);
        tokenManager.initToken(loginUser);
        return loginUser;
    }

    private String initUserDomainInfo(LoginUser loginUser) {
        String username = loginUser.getUsername();
        String domainId = redisService.getCacheObject(AuthConstants.LAST_USING_DOMAIN_KEY_PREFIX + username);
        // 之前没有登录过域，取第一个域
        if (domainId == null) {
            if (UserType.SUPER == loginUser.getUserType()) {
                domainId = domainMapper.selectList(Wrappers.query()).get(0).getId().toString();
            } else {
                domainId = domainMapper.getFirstAttachedDomain(username);
            }
        }
        if (domainId == null) {
            throw new ServiceException("用户没有绑定域");
        }
        loginUser.setDomainId(Long.parseLong(domainId));
        redisService.setCacheObject(AuthConstants.LAST_USING_DOMAIN_KEY_PREFIX + username, domainId);
        return domainId;
    }

    private List<Role> getUserRoles(LoginUser loginUser) {
        // 超级用户权限仅限用户管理、业务域管理等，后续搞成角色特定，目前先设置成拥有所有权限
        if (UserType.SUPER == loginUser.getUserType()) {
            LambdaQueryWrapper<Role> queryWrapper = Wrappers.lambdaQuery(Role.class).eq(Role::getStatus, StatusEnum.NORMAL);
            return roleMapper.selectList(queryWrapper);
        }
        return roleMapper.getUserRoles(loginUser.getUserId());
    }

    public RefreshTokenResp refreshToken(RefreshTokenReq refreshTokenReq) {
        LoginUser loginUser = tokenManager.refreshToken(refreshTokenReq.getRefreshToken(), refreshTokenReq.getUsername());
        if (loginUser == null) {
            throw new ServiceException("Refresh Token 已过期");
        }
        RefreshTokenResp resp = new RefreshTokenResp();
        resp.setAccessToken(loginUser.getAccessToken());
        resp.setRefreshToken(loginUser.getRefreshToken());
        resp.setExpires(loginUser.getExpires());
        resp.setUsername(loginUser.getUsername());
        return resp;
    }

    public void logout() {
        String username = tokenManager.clearTokens();
        log.info("用户{}退出登录", username);
    }

    public LoginUser switchDomain(Long newDomainId) {
        List<Domain> userDomains = domainService.getUserDomains();
        if (userDomains.stream().noneMatch(domain -> domain.getId().equals(newDomainId))) {
            throw new ServiceException("用户没有绑定该域");
        }
        return tokenManager.switchDomain(newDomainId);
    }
}
